﻿using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;

namespace RingBufferStream.Tests
{
  public static class Extensions
  {
    /// <summary>
    /// Convert a struct (or any other value type) to a byte array.
    /// </summary>
    /// <typeparam name="T">Type of the struct to convert.</typeparam>
    /// <param name="structure">Instance of T.</param>
    /// <returns>A byte array representation of the structure.</returns>
    [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes"),
     SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals", MessageId = "bytes")]
    public static byte[] StructToByteArray<T>(this T structure)
    {
      int size = Marshal.SizeOf(typeof(T));
      IntPtr ptr = Marshal.AllocHGlobal(size);
      try
      {
        var bytes = new byte[size];
        Marshal.StructureToPtr(structure, ptr, false);
        Marshal.Copy(ptr, bytes, 0, size);
        return bytes;
      }
      catch (Exception)
      {
        return null;
      }
      finally
      {
        Marshal.FreeHGlobal(ptr);
      }
    }

    /// <cref>ByteArrayToStruct</cref>
    /// <summary>
    /// Convert a byte array to a struct (or any other value type).
    /// </summary>
    /// <typeparam name="T">Type of struct expected from the binary array.</typeparam>
    /// <param name="value">The byte array to parse and convert to T.</param>
    /// <param name="offset">The zero-based offset in the byte array where conversion should start.</param>
    /// <param name="count">The number of byte array elements to convert.</param>
    /// <returns>Instance of T.</returns>
    [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
    public static T ByteArrayToStruct<T>(this byte[] value, int offset = 0, int count = 0)
      where T : struct
    {
      if (value != null)
      {
        int length = count > 0 ? count : Marshal.SizeOf(typeof(T));
        IntPtr ptr = Marshal.AllocHGlobal(length);
        try
        {
          Marshal.Copy(value, offset, ptr, length);
          var structure = (T)Marshal.PtrToStructure(ptr, typeof(T));
          return structure;
        }
        finally
        {
          Marshal.FreeHGlobal(ptr);
        }
      }
      return default(T);
    }

    [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed"), 
     SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
    public static T ByteArrayToStruct<T>(this IEnumerable<ArraySegment<byte>> bytes, int length = 0)
      where T : struct
    {
      if (bytes != null)
      {
        if (length == 0)
        {
          length = Marshal.SizeOf(typeof(T));
        }
        IntPtr ptr = Marshal.AllocHGlobal(length);
        try
        {
          int offset = 0;
          foreach (var segment in bytes)
          {
            Marshal.Copy(segment.Array, segment.Offset, ptr + offset, segment.Count);
            offset += segment.Count;
          }
          return (T)Marshal.PtrToStructure(ptr, typeof(T));
        }
        finally
        {
          Marshal.FreeHGlobal(ptr);
        }
      }
      return default(T);
    }
  }
}